#include <pthread.h>
#include <ctype.h>

#include "socket.h"
#include "control.h"
#include "socket_interface.h"
#include "msg_queue.h"
#include "global.h"

static int s_fd = -1;   //服务器套接字描述符

//初始化TCP服务器套接字
static int tcpsocket_init(void)
{
    s_fd = socket_init(IPADDR, IPPORT);//调用socket_init创建套接字并绑定到指定地址
    return s_fd;        //返回套接字描述符
}

//关闭TCP服务器套接字并清理资源
static void tcpsocket_final(void)
{
    close(s_fd);//关闭套接字
    s_fd = -1;  //重置套接字描述符
}

//接收客户端连接并处理数据
static void* tcpsocket_get(void *arg)
{
    int c_fd = -1;      //客户端套接字描述符
    int ret = -1;       //函数返回值
    struct sockaddr_in c_addr;  //客户端地址信息
    unsigned char buffer[BUF_SIZE]; //接收缓冲区
    mqd_t mqd = -1;             //消息队列描述符
    ctrl_info_t *ctrl_info= NULL;   //控制信息结构体指针

    //TCP Keep-Alive 相关参数设置
    int keepalive = 1; // 开启TCP_KEEPALIVE选项
    int keepidle = 10; // 设置探测时间间隔为10秒
    int keepinterval = 5; // 设置探测包发送间隔为5秒
    int keepcount = 3; // 设置探测包发送次数为3次

    pthread_detach(pthread_self());//分离线程，使其结束后自动回收资源

    printf("%s|%s|%d: s_fd = %d\n", __FILE__, __func__, __LINE__, s_fd);

    //如果服务器套接字未初始化，则尝试初始化
    if (-1 == s_fd)
    {
        s_fd = tcpsocket_init();
        if (-1 == s_fd)
        {
            printf("tcpsocket_init failed\n");//初始化失败打印日志
            pthread_exit(0);    //退出线程
        }
    }

    //获取控制信息中的消息队列描述符
    if (NULL != arg)
        ctrl_info = (ctrl_info_t *)arg;

    if(NULL != ctrl_info)
    {
        mqd = ctrl_info->mqd;
    } 
    if ((mqd_t)-1 == mqd)
    {
        pthread_exit(0);//若消息队列无效则退出线程
    }
  
    memset(&c_addr, 0, sizeof(struct sockaddr_in));//清空客户端地址结构体

    //4. accept
    int clen = sizeof(struct sockaddr_in);
    printf("%s thread start\n", __func__);//打印线程启动信息

    while(1)//主循环等待客户端连接
    {
        //等待客户端连接
        c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
        if (-1 == c_fd)
        {
            continue;//连接失败则继续等待下一个连接请求
        }
        
        // 设置TCP_KEEPALIVE选项
        ret = setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)); 
        if(ret == -1){// 如果设置失败， 打印错误信息并跳出循环    
            perror("setsockopt");
            break;
        }

        // 设置探测时间间隔选项
        ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle)); 
        if(ret == -1){
            perror("setsockopt");// 如果设置失败， 打印错误信息并跳出循环
            break;
        }

        // 设置探测包发送间隔选项
        ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval, sizeof(keepinterval));
        if(ret == -1){
            perror("setsockopt");// 如果设置失败， 打印错误信息并跳出循环
            break;
        }

        //设置探测包发送次数选项
        ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount, sizeof(keepcount));
        if(ret == -1){
            perror("setockopt");// 如果设置失败， 打印错误信息并跳出循环
            break;
        }

        // 打印客户端的IP地址和端口号
        printf("Accepted a connection from %s:%d\n", inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port)); 

        //循环接收客户端发送的数据
        while(1){
            memset(buffer, 0, BUF_SIZE);//清空缓存区
            ret = recv(c_fd, buffer, BUF_SIZE, 0);//接收数据

            //打印接收到的数据内容
            printf("%s|%s|%d: 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n", 
                __FILE__,__func__, __LINE__, 
            buffer[0], buffer[1], buffer[2], buffer[3],buffer[4],buffer[5]);
            
            if(ret > 0)//成功接收到数据
            {
                //判断依据帧格式是否正确
                if(buffer[0] == 0xAA && buffer[1] == 0x55 && 
                    buffer[5] == 0xAA && buffer[4] == 0x55){

                    //再次打印有效数据帧
                    printf("%s|%s|%d: 0x%x, 0x%x,0x%x, 0x%x, 0x%x, 0x%x\n", 
                    __FILE__, __func__, __LINE__, 
                buffer[0], buffer[1], buffer[2], buffer[3],buffer[4],buffer[5]);

                    //将接收到的有效数据发送到消息队列
                    send_message(mqd, buffer, ret);//注意，不要用strlen去计算实际长度
                }
            }
            else if(-1 == ret || 0 == ret){//接收出错或客户端连接
                break;//跳出内存循环，重新等待新连接
            }
        }
    }

    pthread_exit(0);//线程正常退出
}

//定义TCP Socket控制模块实例
struct control tcpsocket_control = {
    .control_name = "tcpsocket",    //控制模块名称
    .init = tcpsocket_init,         //初始化函数
    .final = tcpsocket_final,       //结束函数
    .get = tcpsocket_get,           //数据接收函数（线程主函数）
    .set = NULL,                    //设置函数（未使用）
    .next = NULL                    //链表指针
};

//将TCP Socket控制模块添加到全局控制链表中
struct control *add_tcpsocket_to_ctrl_list(struct control *phead)
{   
    //使用头插法将TCP Socket控制好模块插入到链表中
    return add_interface_to_ctrl_list(phead, &tcpsocket_control);
};